#include "MartSelBase.h"

#include "Joueur.h"

#include "Classe.h"

#include "LireMartiales.h"

#include "SelectionComp.h"
#include "SelectionSort.h"

#include <QtGui/QPushButton>
#include <QtGui/QButtonGroup>

#include <map>
#include <set>

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

const unsigned int MartSelBase::nbrGL = 12;

const vector< string > MartSelBase::nomsGL = list_of
  ("Arbalètes")
  ("Arcs")
  ("Armes d'hast")
  ("Armes d'arts martiaux")
  ("Chaînes et cordes")
  ("Dagues et couteaux")
  ("Epées")
  ("Epieux et javelots")
  ("Gourdin, masses et fléaux")
  ("Haches, pioches et marteaux")
  ("Lances")
  ("Autres armes");

/** Constructeur par défaut.
 * On construit la liste des catégories depuis l'attribut nomsGL. On
 * détecte si le PJ est un combattant, ce qui lui donne alors la
 * possibilité d'acheter un groupe (large ou restreint) en entier.
 *
 * On récupère également un pointeur sur les compétences martiales
 * accessibles, ainsi que leur prix.
 *
 * Il faut vider le conteneur boutons, afin de redéfinir les boutons
 * voulus : "OK", qui sélectionne la catégorie, "Terminer", qui ferme
 * la fenêtre et applique les changements et "Annuler", qui ferme la
 * fenêtre.
 *
 * @param j pointeur sur le Joueur désirant apprendre de nouvelles
 * compétences.
 * @param parent pointeur sur widget parent (orphelin par défaut)
 */
MartSelBase::MartSelBase(Joueur* j, QWidget* parent)
  : BaseMultSel( nbrGL, nomsGL, "Comp. martiales", "MartSelBase", parent ),
    accesGroupes( false )
{
  LireArmesXML = UniqueLireMartiales::instance();

  if ( LireArmesXML == 0 )
    exit( 1 );

  if ( ! const_cast< LireMartiales* >( LireArmesXML ) -> init () )
    exit( 1 );

  if ( LireArmesXML -> groupesLarges() == 0 )
    {
      error() << "Pointeur nul pour les groupes 'larges'." << endmsg;
      exit( 1 );
    }

  joueur = j;

  prepareBoutons();

  setFenetreLayout();
}

/**
 * Constructeur de copie. Une fois le pointeur sur le Joueur récupéré,
 * le travail est laissé à la classe mère et aux méthodes ad-hoc.
 *
 * @param martSel instance de MartSelBase à dupliquer.
 */
MartSelBase::MartSelBase(const MartSelBase& martSel)
  : BaseMultSel( martSel ), accesGroupes( martSel.accesGroupes )
{
  LireArmesXML = UniqueLireMartiales::instance();

  if ( LireArmesXML == 0 )
    exit( 1 );

  if ( ! const_cast< LireMartiales* >( LireArmesXML ) -> init () )
    exit( 1 );

  joueur = martSel.joueur;

  prepareBoutons();

  setFenetreLayout();
}

/**
 * Destructeur, ne fait rien de spécial.
 */
MartSelBase::~MartSelBase()
{}

/**
 *
 */
void MartSelBase::prepareBoutons()
{

  while (! boutons.empty())
    {
      delete boutons.back();
      boutons.pop_back();
    }

  nbrClasses = joueur -> nbrClasses();

  const vector<TypeClasse> *tmpTC;
  map<string, unsigned int> *ptrAcces = &acces;

  for (unsigned int i(0); i < nbrClasses; i++)
    {
      tmpTC = joueur -> classePtr( i ) -> tc();
      if (  std::find( tmpTC -> begin(), tmpTC -> end(), Combattants)
	    != tmpTC -> end())
	accesGroupes = true;
      joueur -> classePtr() -> prixAcces( ptrAcces );
    }
  
  boutons.push_back( new QPushButton( trUtf8("&Quitter"), this ) );
  boutons.back() -> setToolTip( trUtf8("Abandonner le programme") );
  connect( boutons.back()  , SIGNAL( clicked() ), this, SLOT( annuler() ) );
  boutons.push_back( new QPushButton( trUtf8("&Terminer"), this ) );
  boutons.back() -> setToolTip( trUtf8("Quitter la sélection des compétences martiales") );
  connect( boutons.back()  , SIGNAL( clicked() ), this, SLOT( fermer() ) );
  boutons.push_back( new QPushButton( trUtf8("&OK"), this ) );
  boutons.back() -> setToolTip( trUtf8("Sélectionner les compétences de cette catégorie") );
  connect( boutons.back(), SIGNAL( clicked() ), this, SLOT( selectionner() ) );  
}

/** Permet de sélectionner les compétences martiales.
 * Est appelé lorsque le bouton "OK" est pressé. On crée la liste de
 * compétences relatives au groupe sélectionné, avec les coûts. On
 * regarde aussi quelles sont les compétences qui sont déjà connues
 * par le PJ, afin d'éviter qu'il les sélectionne à nouveau.
 *
 * Si le groupe est déjà un groupe restreint, fait appel à
 * selectionArmes, sinon, crée un objet de type SelectionSort afin de
 * déterminer dans quel groupe restreint le personnage veut
 * sélectionner ses armes.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e INFO affiche le nombre de groupes restreints (ou la phrase
 * "C'est déjà un groupe restreint") pour le groupe
 * sélectionné. Affiche un message lors de la sélection d'un groupe
 * complet.\n
 * - \e FUNC affiche les messages de début et de fin de méthode.
 */
void MartSelBase::selectionner()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "MartSelBase::selectionner -> début" << endmsg;

  unsigned int lequel(100), taille(0), i;

  for (i = 0; i < dimension; i++)
    {
      if ( groupe -> button(i) -> isChecked() )
	{
	  lequel = i;
	  break;
	}
    }

  const vector< GLarge > * largesGrp = LireArmesXML -> groupesLarges();

  debug() << largesGrp -> size()
	  <<" groupes trouvés (larges ou restreints)." << endmsg;

  //taille = GroupesLarges[lequel].restreints;
  taille = largesGrp -> at( lequel ) . Restreints;

  if (taille == 0)
    {
      info() << "C'est déjà un groupe restreint" << endmsg;

      selectionArmes( lequel );
    }
  else 
    {
      info() << "Il existe " << taille
	     << " groupes restreints pour ce groupe large" << endmsg;
      // Astuce : faire appel à sélection sort pour déterminer quel groupe
      // restreint on choisit ;-) Voir si ça marche...
     
      vector<string> noms;

      noms.assign( largesGrp -> at( lequel ) . NomsRestreints . begin(),
		   largesGrp -> at( lequel ) . NomsRestreints . end() );
      // for (unsigned int i(0); i < taille; i++)
      //  	noms.push_back( GroupesRestreints[lequel][i] );

      if ( accesGroupes )
	{
	  noms.push_back( "Groupe large en entier" );
	  taille++;
	}

      SelectionSort choisirRestreint( taille, noms,
				      largesGrp -> at( lequel ) . Nom);

      choisirRestreint.setExclusif( true );

      vector<unsigned int> choix;

      StatusCode sc( choisirRestreint.selectionFaite( choix ) );

      if (sc)
	{
	  unsigned int catChoisie( sc.valeur() );
 
	  if ( accesGroupes && catChoisie + 1 == taille)
	    {
	      vector<unsigned int> tmp;
	      StatusCode collect( collecterArmes( lequel, catChoisie + 1, &tmp ) );
	      unsigned int PP( joueur -> nbrPP() );
	      if  ( PP >= 6 )
		{
		  info() << "Choix du groupe large en entier" << endmsg;
		  PP -= 6;
		  joueur -> setPP( PP );
		  joueur -> ajouterMartiales( tmp );
		}
	      else
		warning() << "Pas assez de PP pour sélectionner le groupe en entier"
			  << endmsg;
	    }
	  else
	    {
	      info() << "Groupe restreint choisi : "
		     << largesGrp -> at( lequel ) . NomsRestreints[catChoisie] << endmsg;
	      sc = selectionArmes( lequel, catChoisie );
	    }
	}
      else
	info() << "On arrête là" << endmsg;
    }

  func() << "MartSelBase::selectionner -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Crée un objet de type SelectionComp et le fait apparaître pour
 * sélectionner les compétences martiales par arme ou le groupe
 * restreint en entier si le personnage est un combattant.
 *
 * Fait appel à collecterArmes, qui crée la liste des armes
 * correspondant au groupe (ici restreint) sélectionné.
 *
 * Afin d'éviter que la même arme apparaisse plusieurs fois, on
 * construit en interne un ensemble (set) que l'on teste lors de
 * l'itération sur le conteneur obtenu depuis collecterArmes.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e INFO affiche le nombre de compétences que l'on ajoute ici.\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e DEBUG affiche le prix appliqué.
 *
 * @param large numéro du groupe large à afficher.
 * @param restreint numéro du groupe restreint à afficher. Attention,
 * si 0, cela veut dire que le groupe "large" est en fait un groupe
 * restreint, le premier groupe restreint d'un groupe large est le
 * numéro 1.
 * @return StatusCode \e true si déroulement OK, \e false sinon, le
 * nombre de PP restants se trouve dans valeur().
 */
StatusCode MartSelBase::selectionArmes(const unsigned int& large,
				       const unsigned int& restreint)
{
  /*Debug::*/empileNiveau( &interne );
  func() << "MartSelBase::selectionArmes -> " << endmsg;

  const vector<unsigned int>* ptr;

  unsigned int i, max, prix;

  map<string, unsigned int>::const_iterator mapBegin(acces.begin()),
    mapEnd(acces.end()), mapIt;

  string nomClasse;

  vector<string> noms;
  vector<unsigned int> couts, choix;

  /*
   * Sert à savoir à quelle compétence martiale correspond le numéro
   * que l'on obtient à la sortie de l'objet de type SelectionComp.
   */
  map<unsigned int, unsigned int> correspondance;

  //   for (i = 0; i < nbrTotalArmes; i++)
  //     {
  //       prix = 100;
  //       if (Toutes_armes[i].groupeLarge() != lequel)
  // 	continue;
  //       else if (Toutes_armes[i].nom().substr(0, 1) == "-")
  // 	continue;
  //       else
  // 	{
  // 	  if ( premier > i ) premier = i;
  // 	  if ( dernier < i ) dernier = i;
  // 	  // Rechercher le cout également ici
  // 	  for (mapIt = mapBegin; mapIt != mapEnd; mapIt++)
  // 	    {
  // 	      nomClasse = mapIt -> first;
  // 	      debug() << "On cherche " << nomClasse << endmsg;
  // 	      ptr = joueur -> get_martiale_classe( nomClasse );
  // 	      if (ptr == 0)
  // 		// Combattants => on l'a de toute façon
  // 		{
  // 		  debug() << "Prix = " << mapIt -> second << endmsg;
  // 		  if (mapIt -> second < prix)
  // 		    prix = mapIt -> second;
  // 		}
  // 	      else if (std::find(ptr -> begin(), ptr -> end(), i) !=
  // 		       ptr -> end())
  // 		{
  // 		  debug() << "Prix = " << mapIt -> second << endmsg;
  // 		  if (mapIt -> second < prix)
  // 		    prix = mapIt -> second;;
  // 		}
  // 	    }
  // 	  if (prix < 100)
  // 	    {
  // 	      correspondance[noms.size()] = i;
  // 	      noms.push_back( Toutes_armes[i].nom() );
  // 	      couts.push_back( prix );
  // 	    }
  // 	}
  //     }

  vector<unsigned int> toutRestreint;

  StatusCode sc( collecterArmes( large, restreint, &toutRestreint ) );

  if ( !sc )
    return sc;

  max = toutRestreint.size();

  set< unsigned int> dejaVu;

  for (i = 0; i < max; i++)
    {
      prix = 100;

      if ( dejaVu . find( toutRestreint[i] ) != dejaVu.end() )
	continue;

      for (mapIt = mapBegin; mapIt != mapEnd; mapIt++)
	{
	  nomClasse = mapIt -> first;
	  ptr = joueur -> martialeClasse( nomClasse );
	  if (ptr == 0)
	    // Combattants => on l'a de toute façon
	    {
	      if (mapIt -> second < prix)
		prix = mapIt -> second;
	    }
	  else if (std::find(ptr -> begin(), ptr -> end(), toutRestreint[i]) !=
		   ptr -> end())
	    {
	      if (mapIt -> second < prix)
		prix = mapIt -> second;;
	    }
	}
      if (prix < 100)
	{
	  debug() << "Prix = " << prix << endmsg;
	  correspondance[noms.size()] = toutRestreint[ i ];
	  noms.push_back( LireArmesXML -> listeArmes() -> at( toutRestreint[ i ] ).nom() );
	  couts.push_back( prix );
	  dejaVu . insert( toutRestreint[ i ] );
	}
      else
	warning() << "Arme non accessible pour cette classe" << endmsg;
    }

  if ( accesGroupes )
    {
      noms.push_back("Groupe entier");
      couts.push_back(4); // Le coût est forcément 4 ici ^^
    }

  SelectionComp *armes = new SelectionComp( noms.size(), noms,
					    "Sélection des armes", couts,
					    joueur -> nbrPP(), this );

  armes -> setSelectionGroupe( accesGroupes );

  sc = armes -> selectionFaite(choix);

  if (sc)
    {
      joueur -> setPP( sc.valeur() );
      // Attention, maintenant il faut retrouver les bons numéros!
      // ce qu'il y a dans choix n'est pas le numéro de la compétence
      // martiale, il y a les projectiles, 1/2 mains....

      // Changer le traitement du cas de l'accès au groupe, laisser
      // "normal" dans SelectionComp et changer le post traitement
      // de l'information ici
      vector< unsigned int > choixFinalise;
      vector< unsigned int >::const_iterator itEnd( choix.end() ),
	itBegin( choix.begin() ), it;
      for (it = itBegin; it != itEnd; it++)
	choixFinalise.push_back( correspondance[ *it ] );
      joueur -> ajouterMartiales( choixFinalise );
      info() << "Ajout de " << choix.size() << " compétences" << endmsg;

    }
  else
    info() << "On oublie" << endmsg;

  delete armes;

  func() << "MartSelBase::selectionArmes -> " << endmsg;
  /*Debug::*/depileNiveau();

  return sc;
}

/**
 * Permet de récupérer toutes les armes appartenant à un groupe
 * donné. On peut indifféremment collecter les armes d'un groupe
 * restreint ou large.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e DEBUG affiche les numéros du groupe large et restreint
 * recherché, et affiche également le nom de chaque arme trouvée.
 *
 * @param large numéro du groupe large désiré.
 * @param restreint numéro du groupe restreint désiré (100 si tout le
 * groupe large est voulu).
 * @param liste pointeur sur le conteneur qui sera rempli avec les
 * numéros des armes voulues.
 * @return StatusCode, \e true si OK, \e false sinon.
 */
StatusCode MartSelBase::collecterArmes(const unsigned int& large,
				       const unsigned int& restreint,
				       vector<unsigned int>* liste)
{
  /*Debug::*/empileNiveau( &interne );
  func() << "MartSelBase::collecterArmes -> début" << endmsg;

  unsigned int nbrTotalArmes( LireArmesXML -> listeArmes() -> size() ), i;

  map<string, unsigned int>::const_iterator mapBegin(acces.begin()),
    mapEnd(acces.end()), mapIt;

  const vector<unsigned int>* ptr;

  string nomClasse;

  liste -> clear();

  debug() << "On cherche le groupe " << large << " / " << restreint << endmsg;

  for (i = 0; i < nbrTotalArmes; i++)
    {
      if ( LireArmesXML -> listeArmes() -> at( i ) . groupeLarge() != large)
	continue;
      if (restreint != 100 && LireArmesXML -> listeArmes() -> at( i ).groupeRestreint() != restreint)
	continue;
      else if ( LireArmesXML -> listeArmes() -> at( i ).nom().substr(0, 1) == "-")
	continue;
      else
	{
	  debug() << "On a trouvé " << LireArmesXML -> listeArmes() -> at( i ) . nom() << endmsg;
	  for (mapIt = mapBegin; mapIt != mapEnd; mapIt++)
	    {
	      nomClasse = mapIt -> first;
	      ptr = joueur -> martialeClasse( nomClasse );
	      if (ptr == 0)
		// Combattants => on l'a de toute façon
		liste -> push_back( i );
	      else if (std::find(ptr -> begin(), ptr -> end(), i) !=
		       ptr -> end())
		liste -> push_back( i );
	    }
	}
    }

  
  func() << "MartSelBase::collecterArmes -> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return StatusCode( true, liste -> size() );
}
